#ifndef _GWBASE_CURL_WRAPPER_H_
#define _GWBASE_CURL_WRAPPER_H_

#include <curl/curl.h>
#include <gwbase/base/util.h>
#include <map>
#include <vector>

namespace dicom {

class CurlSList {
 public:
  CurlSList() : list_(NULL) {}
  ~CurlSList() { ::curl_slist_free_all(list_); }

  void AddString(const std::string& s);
  void AddString(const char* s);

  struct curl_slist* list() const { return list_; }

 private:
  struct curl_slist* list_;
};

typedef int (*CurlReadFileCallback)(void* buf, size_t b, size_t n, void* data);
typedef int (*CurlWriteFileCallback)(const void* buf, size_t b, size_t n,
                                     void* data);

// multipart-form
class CurlForm {
 public:
  CurlForm() : first_(NULL), last_(NULL), file_cb_(NULL), file_size_(-1) {}
  ~CurlForm() {
    if (first_ != NULL) ::curl_formfree(first_);
  }

  void AddField(const char* name, const std::string& value);
  void AddFile(const char* name, const std::string& file_name,
               CurlReadFileCallback cb, void* data, int64_t size);

 private:
  friend class EasyCurl;

  void AddToEasyCurl(CURL* handle);

  static int ReadFileCallbackWrapper(void* buf, size_t b, size_t n, void* data);

  curl_httppost* first_;
  curl_httppost* last_;

  CurlReadFileCallback file_cb_;
  void* file_cb_data_;
  size_t file_size_;
};

class CurlHeaders {
 public:
  CurlHeaders() : headers(NULL) {}

  ~CurlHeaders();

  void AddHeader(const char* name, const std::string& val);
  void AddHeader(const char* name, long val);
  void AddHeader(const char* name, uint64_t val);
  void AddOAuthHeader(const std::string& token);

  curl_slist* GetList() const { return headers; }

 private:
  curl_slist* headers;
};

class EasyCurl {
 public:
  EasyCurl();
  ~EasyCurl();

  void Reset();
  void EnableDebugOutput(bool enabled);

  void SetUrl(const std::string& url);

  // must not destroy headers before performing
  void AddHeaders(CurlHeaders* headers);

  // GET/PUT/DELETE and so on
  void SetMethod(const char* method);

  // request data
  void SetUrlEncodeForm(const std::string& data);
  // form must _not_ be released when performing request
  void SetMultipartForm(CurlForm* form);
  void SetPutFileData(CurlReadFileCallback cb, void* data, size_t size);

  // response settings
  void SetWriteFunction(CurlWriteFileCallback cb, void* data);

  // response is saved by callback set by SetWriteFunction()
  bool Perform();

  // response is saved as text
  bool PerformText(std::string* response);

  int http_status() const { return http_status_; }
  const std::string& err_msg() const { return err_msg_; }

 private:
  static int WriteToStringCallback(void* buf, size_t b, size_t n, void* str);

  bool CollectResult(CURLcode code);

  CURL* handle_;
  int http_status_;
  std::string resp_headers_;
  std::string err_msg_;
};
};

#endif  // _GWBASE_CURL_WRAPPER_H_
